''' This module contains basic util classes that give basic functionallity to use in other project modules. '''

from copy import copy

class Enum( set ):
	'''Set that can be used as a constant enumeration.'''
	def __getattr__( self, name ):
		'''Returns name, if it is a value of the set.'''
		if name in self:
			return name
		raise AttributeError

class AttrDict( dict ):
	'''
	Two-level dict that can be used as a constant enumeration, in wich each constant is formed by two parts. 
	'''
	def __init__( self, name=None, root=True ):
		'''
		Initializes the AttrDict node, that could be root or a second-level node.
		
		Argument Keywords:
		name -- Name for the node. It's used to form the two-part constant name. Only second-level nodes should
		have one.
		root -- Indicatse if this is a root node or a second-level node.
		'''
		if not root and not name:
			raise TypeError( "If the dict is not root, it should have a name." )

		self._name = name
		self.root = root

	def __getattr__( self, attr ):
		'''
		If the node is root, returns the second-level node pointed by attr.
		Elsewhere, returns a list containing the node name and the attr.
		'''
		if attr in self.keys():
			if self.root:
				return self[attr]
			else:
				return ( self._name, attr )
		else:
			raise AttributeError( "The key doesn't exist." )

	def __setitem__( self, key, value=None ):
		''' 
		Sets a new value into the AttrDict. This method makes sure more than two-level deep dicts can be created, and that
		no root node can contain actual values, just level-two AttrDict nodes.
		'''
		if self.root and not value:
			dict.__setitem__( self, key, AttrDict( root=False, name=key ) )
		elif not self.root and not isinstance( value, AttrDict ):
			dict.__setitem__( self, key, value )
		else:
			raise TypeError( "The assignation you are trying to do is invalid." )
		
	def copy( self ):
		''' Returns a copy of this AttrDict. '''
		new = AttrDict()
		
		for action in self.keys():
			new[action] = None
			
			for direction in self[action].keys():
				try:
					new[action][direction] = self[action][direction].copy()
				except AttributeError:
					new[action][direction] = copy( self[action][direction] ) 
				
		return new

class Subclasser( type ):
	''' Defines a meta-class that allows to keep track of all subclasses of a class (even indirect subclasses)'''
	#new class attribute that contains a dict binding the subclasses of the class for which this class works as __meta__,
	#with key-strings that identifies them, and that are used by a Factory class to create new instances.
	__inheritors__ = dict()

	@staticmethod
	def __new__( cls, name, bases, dct ):
		''' Adds a new entry with the binding key->subclass'''
		klass = type.__new__( cls, name, bases, dct )
		
		cls.__inheritors__[klass.key] = klass
		
		return klass

class Factory( object ):
	''' Factory for any given class. '''	
	@classmethod
	def new( cls, key, *args, **kwargs ):
		'''
		Instantiates adn returns a specific graphic loader.
		
		Argument keywords:
		key -- key that identifies the class from where to create the object.
		*args -- positional arguments to be passed to the specific class constructor.
		**kwargs -- keyworded arguments to be passed to the specific class constructor
		'''
		if key in cls._BASE_CLASS.__inheritors__.keys():
			return cls._BASE_CLASS.__inheritors__[key]( *args, **kwargs )
